﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Administration;
using System.ServiceModel;
using MS.InformationManagement.DocumentIDServiceApp.Contracts;
using System.Runtime.InteropServices;
using System.IO;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Administration.AccessControl;
using System.Data.SqlClient;
using System.Data;

namespace MS.InformationManagement.DocumentIDServiceApp
{
    [IisWebServiceApplicationBackupBehavior]
    [ServiceBehavior(
        InstanceContextMode = InstanceContextMode.PerSession,
        ConcurrencyMode = ConcurrencyMode.Single,
        IncludeExceptionDetailInFaults = true
    )]
    [Guid("9A4EF31A-69BA-431C-A1DC-C14BA6DA8C21")]
    public class DocumentIDServiceApplication : SPIisWebServiceApplication, IDocumentIDService
    {
        [Persisted]
        private DocumentIDServiceDatabase _database;

        #region [Constructors]

        public DocumentIDServiceApplication() { }

        internal DocumentIDServiceApplication(string name, DocumentIDService service, SPIisWebServiceApplicationPool applicationPool, DocumentIDServiceDatabase database)
            : base(name, service, applicationPool)
        {
            if (database == null)
            {
                throw new ArgumentNullException("Database is null");
            }
            this._database = database;
        }

        #endregion

        #region [Factory methods]

        public static DocumentIDServiceApplication Create(string name, DocumentIDService service, SPIisWebServiceApplicationPool applicationPool, SPDatabaseParameters databaseParameters)
        {
            // Parameters validation
            if (name == null)
                throw new ArgumentNullException("name");
            if (service == null)
                throw new ArgumentNullException("service");
            if (applicationPool == null)
                throw new ArgumentNullException("applicationPool");
            if (databaseParameters == null)
                throw new ArgumentNullException("databaseParameters");

            // Register the database
            DocumentIDServiceDatabase database = new DocumentIDServiceDatabase(databaseParameters);
            database.Update();

            // Create and persist the service application
            DocumentIDServiceApplication serviceApplication = new DocumentIDServiceApplication(
                name,
                service,
                applicationPool,
                database);
            serviceApplication.Update();

            // Register endpoints
            serviceApplication.AddServiceEndpoint("http", SPIisWebServiceBindingType.Http);
            serviceApplication.AddServiceEndpoint("https", SPIisWebServiceBindingType.Https, "secure");
            // serviceApplication.AddServiceEndpoint("tcp", SPIisWebServiceBindingType.NetTcp);
            // serviceApplication.AddServiceEndpoint("pipe", SPIisWebServiceBindingType.NetPipe);

            return (serviceApplication);
        }

        #endregion

        #region [SPIisWebServiceApplication]

        protected override string DefaultEndpointName
        {
            get { return ("http"); }
        }

        public override string TypeName
        {
            get { return ("DocumentID Service Application"); }
        }

        protected override string InstallPath
        {
            get { return Path.GetFullPath(SPUtility.GetGenericSetupPath(@"WebServices\DocumentIDService")); }
        }

        protected override string VirtualPath
        {
            get
            {
                return "DocumentIDService.svc";
            }
        }

        public override Guid ApplicationClassId
        {
            get { return (new Guid("E6295DED-BD78-4D5E-B654-9DFB68531E75")); }
        }

        public override Version ApplicationVersion
        {
            get { return (new Version("1.0.0.0")); }
        }

        public override void Provision()
        {
            base.Status = SPObjectStatus.Provisioning;
            this.Update();

            this._database.Provision();
            base.Provision();

            //base.Status = SPObjectStatus.Online;
            //this.Update();
        }

        public override void Unprovision(bool deleteData)
        {
            base.Status = SPObjectStatus.Unprovisioning;
            this.Update();

            base.Unprovision(deleteData);
            if (deleteData && (this._database != null))
            {
                this._database.Unprovision();
            }

            base.Status = SPObjectStatus.Disabled;
            this.Update();
        }

        protected override void OnProcessIdentityChanged(System.Security.Principal.SecurityIdentifier processSecurityIdentifier)
        {
            base.OnProcessIdentityChanged(processSecurityIdentifier);
            _database.GrantApplicationPoolAccess(processSecurityIdentifier);
        }

        public override SPAdministrationLink ManageLink
        {
            get
            {
                return new SPAdministrationLink(String.Format("/_admin/DocumentIDService/DocumentIDServiceManage.aspx?appid=", this.Id));
            }
        }

        public override SPAdministrationLink PropertiesLink
        {
            get
            {
                return new SPAdministrationLink(String.Format("/_admin/DocumentIDService/DocumentIDServiceProperties.aspx?appid=", this.Id));
            }
        }

        protected override SPNamedCentralAdministrationRights[] AdministrationAccessRights
        {
            get
            {
                return base.AdministrationAccessRights;
            }
        }

        protected override SPNamedIisWebServiceApplicationRights[] AccessRights
        {
            get
            {
                return new SPNamedIisWebServiceApplicationRights[]
                {
                    SPNamedIisWebServiceApplicationRights.FullControl,
                    new SPNamedIisWebServiceApplicationRights("Request", DocumentIDServiceAccessRights.Request),
                    SPNamedIisWebServiceApplicationRights.Read,
                };
            }
        }

        #endregion

        #region [IDocumentIDService]

        [OperationBehavior(Impersonation = ImpersonationOption.Allowed)]
        public void AddDocumentIDBank(string bankName)
        {
            DemandAccess(DocumentIDServiceAccessRights.Request);
            using (SqlConnection session = new SqlConnection(_database.DatabaseConnectionString)) {
                using (SqlCommand command = new SqlCommand("USP_AddDocumentIDBank", session)) 
                {
                    session.Open();
                    command.CommandType = System.Data.CommandType.StoredProcedure;
                    //command.Parameters = new SqlParameter[]{};
                    command.Parameters.Add(new SqlParameter {
                        DbType= DbType.String,
                        Direction = ParameterDirection.Input,
                        ParameterName = "@bankName",
                        Value = bankName
                    });
                    command.ExecuteNonQuery();
                    session.Close();
                }
            }
        }

        [OperationBehavior(Impersonation = ImpersonationOption.Allowed)]
        public void DeleteDocumentIDBank(string bankName)
        {
            DemandAccess(DocumentIDServiceAccessRights.Request);
            using (SqlConnection session = new SqlConnection(_database.DatabaseConnectionString))
            {
                using (SqlCommand command = new SqlCommand("USP_DeleteDocumentIDBank", session))
                {
                    session.Open();
                    command.CommandType = System.Data.CommandType.StoredProcedure;                    
                    command.Parameters.Add(new SqlParameter
                    {
                        DbType = DbType.String,
                        Direction = ParameterDirection.Input,
                        ParameterName = "@bankName",
                        Value = bankName
                    });
                    command.ExecuteNonQuery();
                    session.Close();
                }
            }
        }

        [OperationBehavior(Impersonation = ImpersonationOption.Allowed)]
        public void ResetDocumentIDBank(string bankName)
        {
            DemandAccess(DocumentIDServiceAccessRights.Request);
            using (SqlConnection session = new SqlConnection(_database.DatabaseConnectionString))
            {
                using (SqlCommand command = new SqlCommand("USP_ResetDocumentIDBank", session))
                {
                    session.Open();
                    command.CommandType = System.Data.CommandType.StoredProcedure;
                    command.Parameters.Add(new SqlParameter
                    {
                        DbType = DbType.String,
                        Direction = ParameterDirection.Input,
                        ParameterName = "@bankName",
                        Value = bankName
                    });
                    command.ExecuteNonQuery();
                    session.Close();
                }
            }
        }

        [OperationBehavior(Impersonation = ImpersonationOption.Allowed)]
        public int GetNextDocumentID(string bankName)
        {
            int result = 0;
            DemandAccess(DocumentIDServiceAccessRights.Request);
            using (SqlConnection session = new SqlConnection(_database.DatabaseConnectionString))
            {
                using (SqlCommand command = new SqlCommand("USP_GetNextDocumentID", session))
                {
                    session.Open();
                    command.CommandType = System.Data.CommandType.StoredProcedure;                    
                    command.Parameters.Add(new SqlParameter
                    {
                        DbType = DbType.String,
                        Direction = ParameterDirection.Input,
                        ParameterName = "@bankName",
                        Value = bankName
                    });
                    command.Parameters.Add(new SqlParameter
                    {
                        DbType = DbType.Int32,
                        Direction = ParameterDirection.Output,
                        ParameterName = "@currentConsecutive",
                        Value = bankName
                    });
                    command.ExecuteNonQuery();
                    result = Convert.ToInt32(command.Parameters["@currentConsecutive"].Value);
                    session.Close();
                }
            }
            return result;
        }

        #endregion

        #region [Internal methods]

        internal static DocumentIDServiceApplication GetLocalApplicationByName(string name)
        {
            return DocumentIDService.CurrentInstance.GetWebApplicationByName(name);
        }

        #endregion
    }
}
